package com.nisovin.magicspells.spells.instant;

import java.util.List;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;

import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.materials.MagicMaterial;
import com.nisovin.magicspells.spelleffects.EffectPosition;
import com.nisovin.magicspells.spells.InstantSpell;
import com.nisovin.magicspells.util.MagicConfig;
import com.nisovin.magicspells.util.Util;

public class DowseSpell extends InstantSpell {

	private MagicMaterial material;
	private EntityType entityType;
	private String playerName;
	private int radius;
	private boolean rotatePlayer;
	private boolean setCompass;
	private String strNotFound;
	
	private boolean getDistance;
	
	public DowseSpell(MagicConfig config, String spellName) {
		super(config, spellName);
		
		String blockName = getConfigString("block-type", "");
		if (!blockName.isEmpty()) {
			material = MagicSpells.getItemNameResolver().resolveBlock(blockName);
		}
		String entityName = getConfigString("entity-type", "");
		if (!entityName.isEmpty()) {
			if (entityName.equalsIgnoreCase("player")) {
				entityType = EntityType.PLAYER;
			} else if (entityName.toLowerCase().startsWith("player:")) {
				entityType = EntityType.PLAYER;
				playerName = entityName.split(":")[1];
			} else {
				entityType = Util.getEntityType(entityName);
			}
		}
		
		radius = getConfigInt("radius", 4);
		rotatePlayer = getConfigBoolean("rotate-player", true);
		setCompass = getConfigBoolean("set-compass", true);
		strNotFound = getConfigString("str-not-found", "No dowsing target found.");
		
		getDistance = strCastSelf != null && strCastSelf.contains("%d");
		
		if (material == null && entityType == null) {
			MagicSpells.error("DowseSpell '" + internalName + "' has no dowse target (block or entity) defined");
		}
	}

	@Override
	public PostCastAction castSpell(Player player, SpellCastState state, float power, String[] args) {
		if (state == SpellCastState.NORMAL) {
			
			int distance = -1;
			
			if (material != null) {
			
				Block foundBlock = null;
				
				Location loc = player.getLocation();
				World world = player.getWorld();
				int cx = loc.getBlockX();
				int cy = loc.getBlockY();
				int cz = loc.getBlockZ();
				for (int r = 1; r <= Math.round(radius * power); r++) {
					for (int x = -r; x <= r; x++) {
						for (int y = -r; y <= r; y++) {
							for (int z = -r; z <= r; z++) {
								if (x == r || y == r || z == r || -x == r || -y == r || -z == r) {
									Block block = world.getBlockAt(cx + x, cy + y, cz + z);
									if (material.equals(block)) {
										foundBlock = block;
										break;
									}
								}
							}
							if (foundBlock != null) break;
						}
						if (foundBlock != null) break;
					}
					if (foundBlock != null) break;
				}
							
				if (foundBlock == null) {
					sendMessage(player, strNotFound);
					return PostCastAction.ALREADY_HANDLED;
				} else {
					if (rotatePlayer) {
						Vector v = foundBlock.getLocation().add(.5, .5, .5).subtract(player.getEyeLocation()).toVector().normalize();
						Util.setFacing(player, v);
					}
					if (setCompass) {
						player.setCompassTarget(foundBlock.getLocation());
					}
					if (getDistance) {
						distance = (int)Math.round(player.getLocation().distance(foundBlock.getLocation()));
					}
				}
				
			} else if (entityType != null) {

				// find entity
				Entity foundEntity = null;
				double distanceSq = radius * radius;
				if (entityType == EntityType.PLAYER && playerName != null) {
					// find specific player
					foundEntity = Bukkit.getPlayerExact(playerName);
					if (foundEntity != null) {
						if (!foundEntity.getWorld().equals(player.getWorld())) {
							foundEntity = null;
						} else if (radius > 0 && player.getLocation().distanceSquared(foundEntity.getLocation()) > distanceSq) {
							foundEntity = null;
						}
					}
				} else {
					// find nearest entity
					List<Entity> nearby = player.getNearbyEntities(radius, radius, radius);
					Location playerLoc = player.getLocation();
					for (Entity e : nearby) {
						if (e.getType() == entityType) {
							double d = e.getLocation().distanceSquared(playerLoc);
							if (d < distanceSq) {
								foundEntity = e;
								distanceSq = d;
							}
						}
					}
				}
				
				if (foundEntity == null) {
					sendMessage(player, strNotFound);
					return PostCastAction.ALREADY_HANDLED;
				} else {
					if (rotatePlayer) {
						Location l = (foundEntity instanceof LivingEntity ? ((LivingEntity)foundEntity).getEyeLocation() : foundEntity.getLocation());
						Vector v = l.subtract(player.getEyeLocation()).toVector().normalize();
						Util.setFacing(player, v);
					}
					if (setCompass) {
						player.setCompassTarget(foundEntity.getLocation());
					}
					if (getDistance) {
						distance = (int)Math.round(player.getLocation().distance(foundEntity.getLocation()));
					}
				}
			}
			
			playSpellEffects(EffectPosition.CASTER, player);
			if (getDistance) {
				sendMessage(player, strCastSelf, "%d", distance+"");
				sendMessageNear(player, strCastOthers);
				return PostCastAction.NO_MESSAGES;
			}
		}
		
		return PostCastAction.HANDLE_NORMALLY;
	}

}
